using System;
using System.Collections.Generic;
using System.Reflection;

namespace AGame.TabOrm
{
    public class EntityMapper
    {
        private readonly object _targetObject;

        private readonly Type _targetType;
        private readonly Dictionary<string, IMemberGetSet> _memberGetSetDict = new Dictionary<string, IMemberGetSet>();


        public EntityMapper(object obj)
        {
            _targetObject = obj;
            _targetType = obj.GetType();
        }
        

        private IMemberGetSet GetMemberGetSetImpl(string memberName)
        {
            if (_memberGetSetDict.TryGetValue(memberName, out var memberGetSetImpl))
            {
                return memberGetSetImpl;
            }
            
            var fieldInfo = _targetType.GetField(memberName);
            if (fieldInfo != null)
            {
                return _memberGetSetDict[memberName] = new TabFieldField(fieldInfo);
            }
            var properties = _targetType.GetProperty(memberName);
            if (properties != null)
            {
                return _memberGetSetDict[memberName] = new TabPropertyField(properties);
            }
            return null;
        }


        public T GetValue<T>(string memberName)
        {
            return TryGetValue<T>(memberName, out var value) ? value : default;
        }
        
        
        public EntityMapper GetMapper(string memberName)
        {
            return GetMapper<object>(memberName);
        }
        
        public EntityMapper GetMapper<T>(string memberName)
        {
            if (TryGetValue<T>(memberName, out var value) && value != null)
            {
                return new EntityMapper(value);
            }
            return null;
        }
        

        public bool TryGetValue<T>(string memberName, out T value)
        {
            var getSetImpl = GetMemberGetSetImpl(memberName);
            if (getSetImpl == null || !typeof(T).IsAssignableFrom(getSetImpl.ContentType))
            {
                value = default;
                return false;
            }
            value = (T) getSetImpl.GetValue(_targetObject);
            return true;
        }

        public bool SetValue(string memberName, object value)
        {
            var getSetImpl = GetMemberGetSetImpl(memberName);
            if (getSetImpl == null)
            {
                return false;
            }
            var contentType = getSetImpl.ContentType;
            if (value == null)
            {
                var def = contentType.IsValueType ? Activator.CreateInstance(contentType) : null;
                getSetImpl.SetValue(_targetObject, def);
                return true;
            }
            if (contentType.IsInstanceOfType(value))
            {
                getSetImpl.SetValue(_targetObject, value);
                return true;
            }
            
            return false;
        }

        public void Invoke(string memberName, params object[] param)
        {
            InvokeMethod(memberName, param);
        }
        
        public R Invoke<R>(string memberName, params object[] param)
        {
            var res = InvokeMethod(memberName, param);
            return res is R rRes ? rRes : default;
        }
        
        private object InvokeMethod(string memberName, params object[] param)
        {
            // _targetType.Get
            // _targetType.GetMethod()
            return null;
        }
    }
}